﻿// ===================================
// <copyright>LogoUI Co.</copyright>
// <author>Vlad Spivak</author>
// <email>mailto:vlads@logoui.co.il</email>
// <created>20/00/10</created>
// <lastedit>20/00/10</lastedit>

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
// <remarks>Part of this software based on various internet sources, mostly on the works
// of members of Wpf Disciples group http://wpfdisciples.wordpress.com/
// Also project may contain code from the frameworks: 
//        Nito 
//        OpenLightGroup
//        nRoute
// </remarks>
// ====================================================================================//


using System;
using System.Windows;
using System.ComponentModel;
using System.Collections;
using System.Windows.Interactivity;

#if WP7
//todo:check this;
#endif

namespace LogoFX.Infra.Interactivity
{
    [Obsolete("Use Microsoft.Expression.Interactivity.Core.DataTrigger if possible")]
    public class ValueCompareTrigger
#if WP7
        : BindableTriggerBase<FrameworkElement>
#else
 : TriggerBase<DependencyObject>
#endif
    {
        private const string CANNOT_COMPARE = "Cannot compare, please compare IComparable types or specify an IComparer";

        public static readonly DependencyProperty SourceProperty =
            DependencyProperty.Register("Source", typeof(object), typeof(ValueCompareTrigger),
            new PropertyMetadata(null, new PropertyChangedCallback(OnSourceChanged)));

        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(object), typeof(ValueCompareTrigger),
            new PropertyMetadata(null));

        public static readonly DependencyProperty ComparerProperty =
            DependencyProperty.Register("Comparer", typeof(IComparer), typeof(ValueCompareTrigger),
            new PropertyMetadata(null));

        public static readonly DependencyProperty EqualityProperty =
            DependencyProperty.Register("Equality", typeof(EqualityType), typeof(ValueCompareTrigger),
            new PropertyMetadata(EqualityType.Equals));

        private bool _negate;

        #region Properties

        [Category("Common Properties")]
#if (!WP7)
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
#endif
        public object Source
        {
            get { return GetValue(SourceProperty); }
            set { SetValue(SourceProperty, value); }
        }

        [Category("Common Properties")]
#if WP7
        [TypeConverter(typeof(nRoute.Components.TypeConverters.ConvertFromStringConverter))]
#endif
#if (!WP7)
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
#endif
        public object Value
        {
            get { return (object)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        [Category("Common Properties")]
#if WP7
        [TypeConverter(typeof(nRoute.Components.TypeConverters.ConvertFromStringConverter))]
#endif
#if (!WP7)
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
#endif
        public IComparer Comparer
        {
            get { return (IComparer)GetValue(ComparerProperty); }
            set { SetValue(ComparerProperty, value); }
        }

        [Category("Common Properties")]
        public EqualityType Equality
        {
            get { return (EqualityType)GetValue(EqualityProperty); }
            set { SetValue(EqualityProperty, value); }
        }

        [Category("Common Properties")]
        public bool Negate
        {
            get { return _negate; }
            set { _negate = value; }
        }

#if WP7

        [Category("Common Properties")]
#if (!WP7)
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
#endif
        public Binding SourceBinding
        {
            get { return GetBinding(SourceProperty); }
            set { SetBinding<object>(SourceProperty, value); }
        }

        [Category("Common Properties")]
#if (!WP7)
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
#endif
        public Binding ValueBinding
        {
            get { return GetBinding(ValueProperty); }
            set { SetBinding<object>(ValueProperty, value); }
        }

        [Category("Common Properties")]
#if (!WP7)
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
#endif
        public Binding ComparerBinding
        {
            get { return GetBinding(ComparerProperty); }
            set { SetBinding<IComparer>(ComparerProperty, value); }
        }

        [Category("Common Properties")]
#if (!WP7)
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
#endif
        public Binding EqualityBinding
        {
            get { return GetBinding(EqualityProperty); }
            set { SetBinding<EqualityType>(EqualityProperty, value); }
        }

#endif

        #endregion

        #region Handlers

        private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ValueCompareTrigger)d).CompareValue(e.NewValue);
        }


        private void CompareValue(object sourceValue)
        {
            if (this.AssociatedObject == null) return;      // this is to ensure with initial/static set properties the trigger isn't invoked

            // note, we need to match them per the source type hence the conversion
            bool _result = false;

            // if null, we can only match equality
            if (Value == null)
            {
                if (sourceValue == null)
                {
                    _result = (Equality == EqualityType.Equals || Equality == EqualityType.GreaterThanOrEquals ||
                        Equality == EqualityType.LessThanOrEquals);
                }
            }
            else
            {
                // we compare
                int _compare = 0;
                var _matchValue = ConverterHelper.ConvertToType(Value, sourceValue.GetType());

                if (Comparer != null)
                {
                    _compare = Comparer.Compare(_matchValue, sourceValue);
                }
                else if (_matchValue is IComparable)
                {
                    _compare = (_matchValue as IComparable).CompareTo(sourceValue);
                }
                else
                {
                    throw new InvalidOperationException(CANNOT_COMPARE);
                }

                // per the result we match the equality
                if (_compare == 0)
                {
                    _result = (Equality == EqualityType.Equals ||
                        Equality == EqualityType.GreaterThanOrEquals || Equality == EqualityType.LessThanOrEquals);
                }
                else if (_compare > 0)      // note_ we do opposite, as we check against the source
                {
                    _result = (Equality == EqualityType.LessThan || Equality == EqualityType.LessThanOrEquals);
                }
                else    // _compare < 0 - note we do opposite, as we check against the source
                {
                    _result = (Equality == EqualityType.GreaterThan || Equality == EqualityType.GreaterThanOrEquals);
                }
            }

            // we match with negate and invoke
            if (Negate) _result = !_result;
            if (_result) base.InvokeActions(sourceValue);
        }

        #endregion

        protected override void OnAttached()
        {
            base.OnAttached();
            if(AssociatedObject is FrameworkElement)
                ((FrameworkElement)AssociatedObject).Loaded += AssociatedObjectLoaded;
        }

        protected override void OnDetaching()
        {
            if (AssociatedObject is FrameworkElement)
                ((FrameworkElement)AssociatedObject).Loaded -= AssociatedObjectLoaded;
            base.OnDetaching();
        }

        void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
        {
            CompareValue(Source);
        }

    }
}
